// synch.cc 
//	Routines for synchronizing threads.  Three kinds of
//	synchronization routines are defined here: semaphores, locks 
//   	and condition variables (the implementation of the last two
//	are left to the reader).
//
// Any implementation of a synchronization routine needs some
// primitive atomic operation.  We assume Nachos is running on
// a uniprocessor, and thus atomicity can be provided by
// turning off interrupts.  While interrupts are disabled, no
// context switch can occur, and thus the current thread is guaranteed
// to hold the CPU throughout, until interrupts are reenabled.
//
// Because some of these routines might be called with interrupts
// already disabled (Semaphore::V for one), instead of turning
// on interrupts at the end of the atomic operation, we always simply
// re-set the interrupt state back to its original value (whether
// that be disabled or enabled).
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved.  See copyright.h for copyright notice and limitation 
// of liability and disclaimer of warranty provisions.

#include "copyright.h"
#include "synch.h"
#include "system.h"

//----------------------------------------------------------------------
// Semaphore::Semaphore
// 	Initialize a semaphore, so that it can be used for synchronization.
//
//	"debugName" is an arbitrary name, useful for debugging.
//	"initialValue" is the initial value of the semaphore.
//----------------------------------------------------------------------

Semaphore::Semaphore(char* debugName, int initialValue)
{
    name = debugName;
    value = initialValue;
    queue = new List;
}

//----------------------------------------------------------------------
// Semaphore::Semaphore
// 	De-allocate semaphore, when no longer needed.  Assume no one
//	is still waiting on the semaphore!
//----------------------------------------------------------------------

Semaphore::~Semaphore()
{
    delete queue;
}

//----------------------------------------------------------------------
// Semaphore::P
// 	Wait until semaphore value > 0, then decrement.  Checking the
//	value and decrementing must be done atomically, so we
//	need to disable interrupts before checking the value.
//
//	Note that Thread::Sleep assumes that interrupts are disabled
//	when it is called.
//----------------------------------------------------------------------

void
Semaphore::P()
{
    IntStatus oldLevel = interrupt->SetLevel(IntOff);	// disable interrupts
    
    while (value == 0) { 			// semaphore not available
	queue->Append((void *)currentThread);	// so go to sleep
	currentThread->Sleep();
    } 
    value--; 					// semaphore available, 
						// consume its value
    
    (void) interrupt->SetLevel(oldLevel);	// re-enable interrupts
}

//----------------------------------------------------------------------
// Semaphore::V
// 	Increment semaphore value, waking up a waiter if necessary.
//	As with P(), this operation must be atomic, so we need to disable
//	interrupts.  Scheduler::ReadyToRun() assumes that threads
//	are disabled when it is called.
//----------------------------------------------------------------------

void
Semaphore::V()
{
    Thread *thread;
    IntStatus oldLevel = interrupt->SetLevel(IntOff);

    thread = (Thread *)queue->Remove();
    if (thread != NULL)	   // make thread ready, consuming the V immediately
	scheduler->ReadyToRun(thread);
    value++;
    (void) interrupt->SetLevel(oldLevel);
}


// Dummy functions -- so we can compile our later assignments 
// Note -- without a correct implementation of Condition::Wait(), 
// the test case in the network assignment won't work!
Lock::Lock(char* debugName) {
	
	name = debugName;	//Initialize the name of lock to name assigned
	isLockFree=FREE;	// The lock are free by default. so Intialize isLockFREE to 0
	ownerOfLock=NULL;	// Intially there is no onwer thread on this lock. so it is NULL by default
	waitQueue = new List();	// Instantiating the list for the threads that will wait for lock when it is already held. 

}
Lock::~Lock() {
	delete waitQueue;
}

bool Lock::isHeldByCurrentThread(){

	if(ownerOfLock == currentThread)	// Will check whether the current thread is the same as the on that 
						// is currently holding this lock.
		{

		//printf("Current Thread already has the lock\n");    
		return 1;			// will return 1 if both the threads are same
		}
	else{
		//printf("Current Thread does not have this lock\n");
		return 0;
		}
}

void Lock::Acquire() {
	
	IntStatus old = interrupt -> SetLevel(IntOff);    // Disabling the interrupts before the lock can be acquired by the

						    						  //thread so that the acquirng can atomic.
						    //thread so that the acquirng can atomic.
	

	if(isHeldByCurrentThread())		   // To check whether the thread trying to acquire the lock already has the lock or not.
						   // if they are same there is not need to acquire lock again
						   // and hence the Acquire() function ends here.
	{
		

		DEBUG('z', "\nThread already has the lock...\n");

		//printf("\nThread already has the lock...\n");

		interrupt-> SetLevel(old);        // Restoring the interrupts to existing state after the process of 
						// locking has been completed successfully.
		return;
	}

	//printf("Thread different. Check for Lock availability..\n");

	if(isLockFree == FREE)			// Check if the lock is available for locking or not for the current thread.
	{

	   isLockFree=BUSY;			// Making the lock unavailable for the other threads.
	   ownerOfLock = currentThread;		//Making the current thread the onwer of this lock.
	   DEBUG('z', "Lock acquired by Thread %s  \n\n",currentThread->getName());
	}
	else
	{
		DEBUG('z', "Lock already held by Thread %s . Hence Thread %s has to wait!! \n\n",ownerOfLock->getName(),currentThread->getName());

		waitQueue->Append((void*)currentThread);// Adding the current thread to the wait queue untill the lock is released.
			
		currentThread->Sleep();		// putting the current thread to a sleep untill the lock is released.
	}

	interrupt-> SetLevel(old);		// Restoring the interrupts to existing state after the process of 
						// locking has been completed successfully.
	
	}
void Lock::Release() 
{
	IntStatus old=interrupt->SetLevel(IntOff);				
	if(!(isHeldByCurrentThread()))
	{
		DEBUG('z', "Invalid release call\n");
		interrupt->SetLevel(old);

		return;
	}
	Thread *newThread;
	newThread=(Thread *)waitQueue->Remove();
	if(newThread!=NULL)
	{
		scheduler->ReadyToRun(newThread);
		ownerOfLock=newThread;	
	}
	else
	{
		isLockFree=FREE;
		ownerOfLock=NULL;
	}
	interrupt->SetLevel(old);
}

Condition::Condition(char* debugName)
{
	name=debugName;
	waitingOnLock = NULL;
	waitQueue= new List();
}
Condition::~Condition()
{
	delete waitQueue;

}
void Condition::Wait(Lock* conditionLock) 
{
	IntStatus old=interrupt->SetLevel(IntOff);
	if (conditionLock==NULL) // Thread does not pass any lock. Print a debug message
	{
		DEBUG('z', "conditionLock is NULL");
		interrupt->SetLevel(old);
		return;			
	}

	if(waitingOnLock==NULL) //First time the CV is used.
	{

		waitingOnLock=conditionLock;
	}

	if(waitingOnLock!=conditionLock)// The locks are different. Hence print a error message
	{
		DEBUG('z', "The locks are different");
		interrupt->SetLevel(old);	
		return;
	}

	waitQueue->Append((void*)currentThread);
	conditionLock->Release();
	currentThread->Sleep();
	conditionLock->Acquire();
	interrupt->SetLevel(old);


}
void Condition::Signal(Lock* conditionLock)
{

	IntStatus old= interrupt->SetLevel(IntOff);	
	if(waitQueue->IsEmpty())
	{
		interrupt->SetLevel(old);
		return;											
	}
	if(waitingOnLock!=conditionLock) // Locks are different. Print a debug message
	{
		DEBUG('z', "Waiter and Signaler don't have same lock");
		interrupt->SetLevel(old);	
		return;
	}
	Thread *threadUponSignal;
	threadUponSignal=(Thread *)waitQueue->Remove();	


	scheduler->ReadyToRun(threadUponSignal);	
	if(waitQueue->IsEmpty())		
	{
		waitingOnLock=NULL;		
	}
	interrupt->SetLevel(old);		

}




void Condition::Broadcast(Lock* conditionLock) 
{
	while(!(waitQueue->IsEmpty()))
	{
		Signal(conditionLock);	
	}

}
